home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of Sound
/
World of Sound.iso
/
utils
/
miditools
/
midiwatcher
/
midiwatcher.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-14
|
15KB
|
570 lines
/* MIDIwatcher v1.1, copyright ©1993 by Ed Mackey. */
/* Source and binary are freely distributable in unmodified form. */
/*
Launch programs by playing tunes!
Based on "hr" from midi.library, and requires midi.library.
Shows proper method of waiting for, receiving, processing, and
disposing of MidiPackets.
*/
#include <stdio.h>
#include <libraries/dos.h>
#include <exec/lists.h>
#include <clib/macros.h>
#include <midi/midi.h>
#include <functions.h>
#include <rexx/rxslib.h>
struct library *RexxBase = NULL;
struct MsgPort *myport = NULL, *RexxPort = NULL;
struct RexxMsg *mymsg = NULL;
struct RexxArg *myarg = NULL;
void *MidiBase;
struct MDest *dest;
struct MRoute *route;
struct MidiPacket *packet; /* buffer this in case we get shut down before freeing the current message */
long record,tunes,debugON,OutThereMsgs,LineLen;
FILE *myfile;
char *sname = "MidiIn";
char *fname = "S:midiwatch";
#define MAXTUNES 50
#define MAXCHARS 200
struct NFAnode
{
UWORD note;
WORD failnum;
struct NFAnode *next;
} starters[MAXTUNES];
struct program
{
char *port;
char *msg;
char *cmd;
} progs[MAXTUNES], failprogs[MAXTUNES];
char tmp[MAXCHARS+2];
struct List InTunes;
struct InTune
{
struct Node node;
struct NFAnode *note;
WORD failnum;
WORD tunenum; /* just for "-d" debug purposes */
long pos; /* likewise */
};
_abort(){} /* abort routine called when CTRL-C is hit (Aztec) */
/* The next routine is run if MidiWatcher tries to exit when there are
* ARexx messages that have been sent but not returned.
*/
void WaitForOuts()
{
ULONG flags = (1L << myport->mp_SigBit);
printf("Waiting for ARexx messages to return...\n");
while (OutThereMsgs > 0) {
while (mymsg = (void *)GetMsg(myport)) /* assignment intended */
{
ClearRexxMsg(mymsg,1);
DeleteRexxMsg(mymsg);
OutThereMsgs--;
}
if (OutThereMsgs > 0) Wait(flags);
}
mymsg = NULL;
printf("Ah, all done.\n");
}
void cleanup(int exc)
{
if (mymsg) DeleteRexxMsg(mymsg);
if (packet) FreeMidiPacket (packet);
if (route) DeleteMRoute (route);
if (dest) DeleteMDest (dest);
if (MidiBase) CloseLibrary (MidiBase);
if (OutThereMsgs > 0) WaitForOuts();
if (myport) DeletePort(myport);
if (RexxPort)
{
Forbid();
while (mymsg = (void *)GetMsg(RexxPort)) /* Assignment intended */
{
mymsg -> rm_Result1 = mymsg -> rm_Result2 = 0;
ReplyMsg((void *)mymsg);
}
DeletePort(RexxPort);
Permit();
}
if (RexxBase) CloseLibrary (RexxBase);
if (myfile) fclose(myfile);
exit(exc);
}
void die(int exc, char *msg)
{
if (msg) printf("%s\n",msg);
cleanup(exc);
}
#if 0
dump (s,len) /* print len bytes in hex */
UBYTE *s;
int len;
{
while (len--) printf ("%02x ",*s++);
}
#endif
/* The next routine takes action when a tune is recognized, or when there
* is a failure past the "point of no return".
*/
void ExeProg(UWORD prognum, int FailYesNo)
{
struct program *tp;
struct MsgPort *mp;
if (FailYesNo)
tp = failprogs + prognum;
else
tp = progs + prognum;
if (debugON)
{
if (FailYesNo)
printf("Running failure program %d\n",prognum);
else
printf("Running program %d\n",prognum);
if (tp -> port) printf("ARexx port : %s\n",tp -> port);
if (tp -> msg) printf("ARexx msg : %s\n",tp -> msg);
if (tp -> cmd) printf("CLI command: %s\n\n",tp -> cmd);
}
if ((tp -> port) && (tp -> msg))
{
if (!RexxBase)
printf("I need " RXSNAME " for ARexx messages!\n");
else
{
mymsg = CreateRexxMsg(myport,NULL,tp -> port);
if (!mymsg) die(10,"Out of mem!");
mymsg -> rm_Args[0] = (void *)(tp -> msg);
if (!FillRexxMsg(mymsg,1,0)) die(10,"Out of mem");
Forbid();
if (mp = FindPort(tp -> port)) /* assignment intended */
{
PutMsg(mp,(void *)mymsg);
Permit();
OutThereMsgs++;
} else {
Permit();
ClearRexxMsg(mymsg,1);
DeleteRexxMsg(mymsg);
}
mymsg = NULL;
}
}
if (tp -> cmd) Execute((tp -> cmd),0,0);
}
/* The next routine handles incoming MIDI messages. */
int dopak (UBYTE *s, int len) /* Message pointer, Message length */
{
struct InTune *tp, *t2;
struct NFAnode *np, *n2;
int t;
if (len < 2) return(0); /* Too short? Ignore it. */
if ((s[0] < 0x90) || (s[0] > 0x9f)) return(0); /* Not a NoteDn? Ignore it! */
if (record) /* RECORD MODE */
{
if ((LineLen += 3) > 75)
{
fprintf(myfile,"%02x\n ",s[1]);
LineLen = 0;
} else {
fprintf(myfile,"%02x ",s[1]);
}
} else { /* PLAYBACK MODE */
tp = (void *)InTunes.lh_Head;
while (t2 = (void *)(tp -> node.ln_Succ)) /* Assignment intended! */
{
++(tp -> pos);
np = tp -> note;
n2 = NULL;
if (s[1] == (np -> note)) n2 = np -> next;
tp -> note = n2;
if (n2)
{
if (debugON) printf("Tune %d:%d continues. Matched note %02x.\n",
tp -> tunenum, tp -> pos, s[1]);
if ((n2 -> failnum) >= 0)
{
tp -> failnum = n2 -> failnum;
if (debugON) printf("Set failure of %d:%d to %d.\n",
tp -> tunenum, tp -> pos, tp -> failnum);
}
if ((n2 -> note) > 255)
{
if (debugON) printf("Tune %d completed!\n",
tp -> tunenum);
tp -> failnum = -1;
ExeProg((n2 -> note) - 256, 0);
}
} else {
if (debugON && ((np -> note) < 0x100))
printf("Tune %d:%d failed. Expecting note %02x, got note %02x.\n",
tp -> tunenum, tp -> pos, np -> note, s[1]);
if (tp -> failnum >= 0) ExeProg(tp -> failnum, 1);
Remove((void *)tp);
free((void *)tp);
}
tp = t2;
}
np = starters;
for (t = 0; t < tunes; t++)
{
/* This one just got too messy. */
// if (debugON) printf("Checking start note %04x, got note %04x\n",np -> note, s[1]);
if ((np -> note) == s[1])
{
if (debugON) printf("Started tune %d\n",t);
if (!(tp = (void *)malloc(sizeof(struct InTune)))) die(10,"Mem!!");
tp -> tunenum = t;
tp -> pos = 1;
n2 = tp -> note = np -> next;
tp -> failnum = n2 -> failnum;
if ((n2 -> note) > 255)
{
ExeProg((n2 -> note) - 256, 0);
free((void *)tp);
} else {
AddTail(&InTunes,(void *)tp);
}
}
np++;
}
}
}
dumppacket (packet)
struct MidiPacket *packet;
{
if (packet->Type == MMF_SYSEX) { /* if it's a System Exclusive message... */
// dump (packet->MidiMsg,MIN(packet->Length,8)); /* only print the first 8 bytes */
// printf ("... (%d bytes)\n",packet->Length);
; /* or just ignore it ;-) */
}
else { /* otherwise */
dopak (packet->MidiMsg,packet->Length); /* process the message */
}
}
void Suspend()
{
ULONG flags = SIGBREAKF_CTRL_C | (1L << myport->mp_SigBit) | (1L << RexxPort->mp_SigBit);
int KGS = 0;
if (route) DeleteMRoute (route);
route = NULL;
if (dest) DeleteMDest (dest);
dest = NULL;
while ((!KGS) && (!(Wait(flags) & SIGBREAKF_CTRL_C))) {
while (mymsg = (void *)GetMsg(myport)) /* assignment intended */
{
ClearRexxMsg(mymsg,1);
DeleteRexxMsg(mymsg);
OutThereMsgs--;
}
while (mymsg = (void *)GetMsg(RexxPort)) /* Assignment intended */
{
tmp[0] = 0;
if (strlen((void *)(mymsg -> rm_Args[0])) < MAXCHARS)
strcpy(tmp,(void *)(mymsg -> rm_Args[0]));
mymsg -> rm_Result1 = mymsg -> rm_Result2 = 0;
ReplyMsg((void *)mymsg);
mymsg = NULL;
if (!strcmp(tmp,"QUIT")) die(0,NULL);
if (!strcmp(tmp,"RESUME")) KGS = 1;
}
}
if (!KGS) die(0,NULL); /* CTRL-C check */
/* create our dest node (private) */
if (!(dest = CreateMDest (NULL,NULL))) {
printf ("can't create Dest\n");
cleanup(10);
}
/* create our route to MidiIn or whatever the user specified */
if (!(route = MRouteDest (sname, dest, NULL))) { /* use default route on our MDest (defaults to everything) */
printf ("can't create Route (can't find source \"%s\"?)\n",sname);
cleanup(10);
}
}
process ()
{
ULONG flags = SIGBREAKF_CTRL_C | (1L << dest->DestPort->mp_SigBit) | (1L << myport->mp_SigBit);
if (RexxPort) flags |= (1L << RexxPort->mp_SigBit);
while (!(Wait(flags) & SIGBREAKF_CTRL_C)) { /* wait until we get a message or CTRL-C is hit, quit on CTRL-C */
while (packet = GetMidiPacket (dest)) { /* get a packet */
dumppacket (packet); /* print it */
FreeMidiPacket (packet); /* free it */
}
while (mymsg = (void *)GetMsg(myport)) /* assignment intended */
{
ClearRexxMsg(mymsg,1);
DeleteRexxMsg(mymsg);
OutThereMsgs--;
}
if (RexxPort) {
while (mymsg = (void *)GetMsg(RexxPort)) /* Assignment intended */
{
tmp[0] = 0;
if (strlen((void *)(mymsg -> rm_Args[0])) < MAXCHARS)
strcpy(tmp,(void *)(mymsg -> rm_Args[0]));
mymsg -> rm_Result1 = mymsg -> rm_Result2 = 0;
ReplyMsg((void *)mymsg);
mymsg = NULL;
if (!strcmp(tmp,"QUIT")) return();
if (!strcmp(tmp,"SUSPEND"))
{
Suspend();
flags = SIGBREAKF_CTRL_C |
(1L << dest->DestPort->mp_SigBit) |
(1L << myport->mp_SigBit) |
(1L << RexxPort->mp_SigBit);
}
}
}
}
}
/* The next routine makes an NFA (Nondeterministic Finite-state Automaton) out
* of the user's tunes. I learned about them in CSc 318 at Lehigh, with
* professor Ed Kay. I always meant to do an NFA -> DFA conversion, so as to
* take less CPU time during the actual realtime processing of incoming notes.
* I learned the conversion in 318 also, but I never had the time or energy to
* implement it in the context of this silly program.
*
* Feel free to try it yourself.
* If you succeed, you _must_ send me a copy.
*/
void MakeNFA()
{
long mode,ch,hex;
char *tmp2, *tp;
struct NFAnode *n1, *n2;
mode = -1L;
while ((ch = fgetc(myfile)) != EOF)
{
if ((!mode) && (((ch >= '0') && (ch <= '9')) || ((ch >= 'a') && (ch <= 'f'))))
{
tmp[0] = ch; tmp[1] = fgetc(myfile); tmp[2] = 0;
sscanf(tmp,"%lx",&hex);
n2 = (void *)malloc(sizeof(struct NFAnode));
if (!n2) die(10,"Out of mem");
n2 -> failnum = -1;
n1 -> note = hex;
n1 -> next = n2;
n2 -> note = tunes + 256;
n2 -> next = NULL;
n1 = n2;
} else if (mode > 0) {
if (ch == 10)
{
*tp = 0;
if (tmp[0])
{
tmp2 = (void *)malloc(strlen(tmp) + 1);
if (!tmp2) die(10,"Out of mem");
strcpy(tmp2,tmp);
switch(mode)
{
case 1: progs[tunes].cmd = tmp2; break;
case 2: progs[tunes].port = tmp2; break;
case 3: progs[tunes].msg = tmp2; break;
case 4: failprogs[tunes].cmd = tmp2; break;
case 5: failprogs[tunes].port = tmp2; break;
case 6: failprogs[tunes].msg = tmp2; break;
}
}
mode = -1;
} else {
*(tp++) = ch;
if ((ULONG)tp >= (ULONG)(tmp + MAXCHARS)) tp--;
}
} else if (mode == -2) {
if (ch == 10) mode = -1;
} else switch(ch) {
case '$': mode = 0; tmp[0] = 0;
if ((++tunes) >= MAXTUNES) die(10,"Too many tunes");
progs[tunes].port = NULL;
progs[tunes].msg = NULL;
progs[tunes].cmd = NULL;
failprogs[tunes].port = NULL;
failprogs[tunes].msg = NULL;
failprogs[tunes].cmd = NULL;
n1 = &(starters[tunes]);
n1 -> note = 0;
n1 -> next = NULL;
n1 -> failnum = -1;
break;
case ':': if (!mode) n1 -> failnum = tunes; break;
case '>': mode = 1; tp = tmp; tmp[0] = 0; break;
case '@': mode = 2; tp = tmp; tmp[0] = 0; break;
case '*': mode = 3; tp = tmp; tmp[0] = 0; break;
case ']': mode = 4; tp = tmp; tmp[0] = 0; break;
case '+': mode = 5; tp = tmp; tmp[0] = 0; break;
case '!': mode = 6; tp = tmp; tmp[0] = 0; break;
case '#': mode = -2; break;
}
}
tunes++;
}
main(argc,argv)
char **argv;
{
int t;
record = OutThereMsgs = debugON = LineLen = 0;
myfile = NULL;
tunes = -1;
if (argc > 1 && *argv[1] == '?') {
printf("MidiWatcher v1.1 by Ed Mackey. Watch MIDI keyboard for tunes.\n");
printf("Usage: %s [-i source] [-f savefile] [-r] [-d]\n",argv[0]);
printf(" -i selects a MidiIn source, defaults to MidiIn\n");
printf(" -f selects a tune save file, defaults to S:midiwatch\n");
printf(" -r turns on RECORD mode and adds a tune to file -f\n");
printf(" -d turns on DEBUG mode (playback only) to see your tunes go.\n");
exit (1);
}
if (!(MidiBase = OpenLibrary (MIDINAME,MIDIVERSION))) {
printf ("can't open midi.library\n");
cleanup(10);
}
RexxBase = OpenLibrary(RXSNAME,0L); /* RexxBase may be NULL after this! */
myport = CreatePort(NULL,0);
if (!myport) die(10,"No mem for replyport");
t = 1;
while (argc > t)
{
if (argv[t][0] == '-')
{
switch(argv[t][1])
{
case 'i': if (argv[t][2]) sname = &(argv[t][2]);
else sname = argv[++t];
break;
case 'f': if (argv[t][2]) fname = &(argv[t][2]);
else fname = argv[++t];
break;
case 'r': record = 1; break;
case 'd': debugON = 1; break;
}
}
t++;
}
/* create our dest node (private) */
if (!(dest = CreateMDest (NULL,NULL))) {
printf ("can't create Dest\n");
cleanup(10);
}
/* create our route to MidiIn or whatever the user specified */
if (!(route = MRouteDest (sname, dest, NULL))) { /* use default route on our MDest (defaults to everything) */
printf ("can't create Route (can't find source \"%s\"?)\n",sname);
cleanup(10);
}
if (record)
{
myfile = fopen(fname,"a");
printf("Play a tune and press CTRL-C\n");
} else {
myfile = fopen(fname,"rb");
}
if (!myfile)
{
printf("Unable to open file %s\n",fname);
cleanup(10);
}
NewList(&InTunes);
if (record)
fprintf(myfile,"# Your comments go here (such as name of tune)...\n"
"# You may wish to insert a colon (\":\") at the point of no return...\n$");
else
{
MakeNFA();
if (debugON) printf("Loaded %d tunes\n",tunes);
if (RexxBase)
{ /* If MidiWatcher is already running, try to kill it. */
struct MsgPort *mp;
mymsg = CreateRexxMsg(myport,NULL,"MIDIWATCHER");
if (!mymsg) die(10,"Out of mem!");
mymsg -> rm_Args[0] = (void *)("QUIT");
if (!FillRexxMsg(mymsg,1,0)) die(10,"Out of mem");
Forbid();
if (mp = FindPort("MIDIWATCHER")) /* assignment intended */
{
PutMsg(mp,(void *)mymsg);
Permit();
OutThereMsgs++;
} else {
Permit();
ClearRexxMsg(mymsg,1);
DeleteRexxMsg(mymsg);
}
mymsg = NULL;
}
RexxPort = CreatePort("MIDIWATCHER",4L);
if (!RexxPort) die(10,"No mem for port");
}
process(); /* process until shutdown */
if (record)
{
printf("Saving and exiting...\n");
fprintf(myfile,"\n"
"#### Success program...\n"
"@PLAY\n"
"*id midi_event\n"
">echo \"Tune recognized\"\n"
"#### Failure program...\n"
"+PLAY\n"
"!id midi_fail\n"
"]echo \"You passed the colon, and then blew it!!\"\n\n");
}
cleanup(0);
}
/* End of midiwatcher.c */